/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tencent.tendon.convert.json;

import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author nbzhang
 */
public class JsonMapDecoder<K, V> extends JsonDecoder<Map<K, V>> {

    private final JsonDecodable<K> keydecoder;

    private final JsonDecodable<V> valdecoder;

    JsonMapDecoder(final Type type) {
        super(parseRawType(type));
        if (type instanceof ParameterizedType) {
            final Type[] pt = ((ParameterizedType) type).getActualTypeArguments();
            this.keydecoder = JsonLoader.loadDecodable(pt[0]);
            this.valdecoder = JsonLoader.loadDecodable(pt[1]);
        } else {
            throw new JsonException("not support the type (" + type + ")");
        }
        this.simpled = valdecoder.isSimpled();
    }

    private JsonMapDecoder(Class<Map<K, V>> type, JsonDecodable<K> keydecoder, JsonDecodable<V> valdecoder) {
        super(type);
        this.keydecoder = keydecoder;
        this.valdecoder = valdecoder;
        this.simpled = keydecoder.isSimpled() && valdecoder.isSimpled();
    }

    public static <K, V> JsonMapDecoder<K, V> create(Class<Map<K, V>> type, JsonDecodable<K> keydecoder, JsonDecodable<V> valdecoder) {
        return new JsonMapDecoder(type, keydecoder, valdecoder);
    }

    @Override
    public Map<K, V> convertFrom(JsonReader in) {
        in.checkObject();
        final Map<K, V> result = creator.create();
        if (!simpled) in.offer(result);
        while (in.hasNext()) {
            K key = keydecoder.convertFrom(in);
            in.checkColon();
            V value = valdecoder.convertFrom(in);
            result.put(key, value);
        }
        if (!simpled) in.poll();
        return result;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + "{keydecoder : " + keydecoder + ", valdecoder : " + valdecoder + "}";
    }

    private static Class parseRawType(final Type type) {
        if (type instanceof ParameterizedType) {
            return parseRawType(((ParameterizedType) type).getRawType());
        } else if (type instanceof Class && Map.class.isAssignableFrom((Class) type)) {
            Class clazz = (Class) type;
            if (clazz.isAssignableFrom(HashMap.class)) return HashMap.class;
            return clazz;
        } else {
            throw new JsonException("not support the type (" + type + ")");
        }
    }
}